/*CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
 C      ALU.c
 C      Application Name: Arithmetic Logic Unit Simulator
 C      Programmers Names: Tony Butterfield, Evan Phillips
 C      Date Completed: 2/14/11
 C      Brief Description: This program implements the ALU ADT and also contains the previously
 C		completed implementation of the Register and RegisterFile ADT's.
 CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC*/

#include <stdlib.h>
#include <stddef.h>
#include <stdio.h>
#include <string.h>

#include "ALU.h"


//the below are needed to avoid multiple file compilation
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 + Register16 ADT implementation 
 +
 + Basic operations
 +      Initialize: creates register object and puts a random value from 0 to 65535 into it.
 +      GetLowByte: returns an unsigned char (uchar) from LOB of register
 +      GetHIByte: returns a uchar from the HOB of register
 +      PutLowByte: puts a uchar into the LOB of the register
 +      PutHIByte: puts a uchar into the HOB of the register
 +      SignOf: returns bit 15 value in a uchar
 +      Destructor: not needed
 + Composite Operations
 +      GetRegValue: returns a ushort, the value in the register
 +      PutRegValue: returns error, puts ushort into register
 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/

/*=================================================================================================
 = Comment block for function: register_initialize
 = Programmer names: Tony Butterfield, Evan Phillips
 = Final test and approval date: 1/22/11
 = Synopsis:
 =      RegisterPtr register_initialize(void)
 =      Return value ranges: Initialized pointer to register
 =      Parameter value ranges: N/a
 =      Error conditions: N/a
 = Initializes a RegisterPtr to a random value.
 ==================================================================================================*/
RegisterPtr register_initialize(void) {
        RegisterPtr rp = (RegisterPtr) malloc(sizeof(ushort));
        return rp;
}

/*=================================================================================================
 = Comment block for function: register_getLowByte
 = Programmer names: Tony Butterfield, Evan Phillips
 = Final test and approval date: 1/22/11
 = Synopsis:
 =      uchar register_getLowByte(RegisterPtr rp, int * error)
 =      Return value ranges: A uchar containing the low order byte of the Register pointed at by rp
 =      Parameter value ranges: Pointer to a Register, pointer to an integer
 =      Error conditions: rp is null pointer
 = Retrieves the low order byte of the Register being pointed at by a RegisterPtr.
 ==================================================================================================*/
uchar register_getLowByte(RegisterPtr rp, int * error) {
        if (rp == NULL) {
                *error = NULL_ERR;
                return 0;
        }
        return *rp & LOB_MASK;
}

/*=================================================================================================
 = Comment block for function: register_getHiByte
 = Programmer names: Tony Butterfield, Evan Phillips
 = Final test and approval date: 1/22/11
 = Synopsis:
 =      uchar register_getHiByte(RegisterPtr rp, int * error)
 =      Return value ranges: A uchar containing the high order byte of the Register pointed at by rp
 =      Parameter value ranges: Pointer to a register, pointer to an integer
 =      Error conditions: rp is a null pointer
 = Retrieves the high order byte of the Register being pointed at by a RegisterPtr.
 ==================================================================================================*/
uchar register_getHiByte(RegisterPtr rp, int * error) {
        if (rp == NULL) {
                *error = NULL_ERR;
                return 0;
        }
        return *rp >> 8; //since were casting down the data we want to keep needs to be in the rightmost byte
}

/*=================================================================================================
 = Comment block for function: register_putLowByte
 = Programmer names: Tony Butterfield, Evan Phillips
 = Final test and approval date: 1/22/11
 = Synopsis:
 =      int register_putLowByte(RegisterPtr rp, uchar byte)
 =      Return value ranges: Error code: 0 (success) or NULL_ERR
 =      Parameter value ranges: Pointer to a register, data you wish to insert (0 -> 2^8-1)
 =      Error conditions: rp is a null pointer
 = Puts a byte of data into the low order byte of a Register being pointed at by a RegisterPtr.
 ==================================================================================================*/
int register_putLowByte(RegisterPtr rp, uchar byte) {
        if (rp == NULL)
                return NULL_ERR;
        Register result = *rp;
        result = result & HOB_MASK; //clear out the low order byte of whats already there
        result = result | byte; //now put our value in there with a bitwise or
        *rp = result;
        return 0; //success!
}

/*=================================================================================================
 = Comment block for function: register_putHiByte
 = Programmer names: Tony Butterfield, Evan Phillips
 = Final test and approval date: 1/22/11
 = Synopsis:
 =      int register_putHiByte(RegisterPtr rp, uchar byte)
 =      Return value ranges: Error code: 0 (success) or NULL_ERR
 =      Parameter value ranges: Pointer to register, data you wish to insert (0 -> 2^8-1)
 =      Error conditions: rp is a null pointer
 = Puts a byte of data into the high order byte of a Register being pointed at by a RegisterPtr.
 ==================================================================================================*/
int register_putHiByte(RegisterPtr rp, uchar byte) {
        if (rp == NULL)
                return NULL_ERR;
        Register result = *rp;
        ushort temp = byte << 8; //to use this ORing method, the data needs to be in the leftmost byte (hope gcc fills shifts with zeros :D)
        result = result & LOB_MASK; //clear high order byte
        result = result | temp; //or value in
        *rp = result;
        return 0; //success
}

/*=================================================================================================
 = Comment block for function: register_signOf
 = Programmer names: Tony Butterfield, Evan Phillips
 = Final test and approval date: 1/22/11
 = Synopsis:
 =      uchar register_signOf(RegisterPtr rp)
 =      Return value ranges: 0 if the data is positive, 1 if it is negative, or NULL_ERR
 =      Parameter value ranges: Pointer to a register
 =      Error conditions: rp is a null pointer
 = Determines the sign of the data held in a Register being pointed at by a RegisterPtr.
 ==================================================================================================*/
uchar register_signOf(RegisterPtr rp) {
        if (rp == NULL)
                return NULL_ERR;
        return *rp & BIT_15 ? 1 : 0;
        //if *rp & BIT_15 is greater than 0 than the 15th bit must be set so we return 1, otherwise return zero
}

/*=================================================================================================
 = Comment block for function: register_getRegValue
 = Programmer names: Tony Butterfield, Evan Phillips
 = Final test and approval date: 1/22/11
 = Synopsis:
 =      ushort register_getRegValue(RegisterPtr rp, int * error)
 =      Return value ranges: The data held in the Register pointed at by rp (16 bits)
 =      Parameter value ranges: Pointer to a register, pointer to an int
 =      Error conditions: rp is a null pointer
 = Retrieves all 16 bits of data from a Register being pointed at by a RegisterPtr.
 ==================================================================================================*/
ushort register_getRegValue(RegisterPtr rp, int * error) {
        if (rp == NULL) {
                *error = NULL_ERR;
                return 0;
        }
        ushort result = *rp;
        return result;
}

/*=================================================================================================
 = Comment block for function: register_putRegValue
 = Programmer names: Tony Butterfield, Evan Phillips
 = Final test and approval date: 1/22/11
 = Synopsis:
 =      int register_puteRegValue(RegisterPtr rp, ushort word)
 =      Return value ranges: Error code: 0 (success) or NULL_ERR
 =      Parameter value ranges: Pointer to register, data to be put in register (0 -> 2^16)
 =      Error conditions: rp is a null pointer
 = Puts a full word of data into a Register being pointed at by RegisterPtr.
 ==================================================================================================*/
int register_putRegValue(RegisterPtr rp, ushort word) {
        if (rp == NULL) {
                return NULL_ERR;
        }
        *rp = word;
        return 0;
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 + SC-2 RegisterFile
 + Basic Operations:
 +      Initialize: creates a register file of 16 register objects in an array
 +      GetByteFrom: returns a uchar from a designated register and LOB or HOB
 +      PutByteTo: puts a single byte to either LOB or HOB or register
 + Composite Operations:
 +      GetWordFrom: returns a ushort, the content of the designated register
 +      PutWordTo: puts the ushort into the designated register
 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/

/*=================================================================================================
 = Comment block for function: registerFile_initialize
 = Programmer names: Tony Butterfield, Evan Phillips
 = Final test and approval date: 1/23/11
 = Synopsis:
 =      RegisterFilePtr registerFile_initialize()
 =      Return value ranges: Pointer to the created register file
 =      Parameter value ranges: N/A
 =      Error conditions: N/A
 = Initializes and returns a RegisterFilePtr to a random value.
 ==================================================================================================*/
RegisterFilePtr registerFile_initialize() {
        RegisterFilePtr rfptr = (RegisterFilePtr) malloc(sizeof(RegisterPtr) * REGISTER_COUNT);
        int i = 0;
        for (i; i < REGISTER_COUNT; i++) {
                rfptr[i] = (RegisterPtr) register_initialize();
        }
        return rfptr;
}

/*=================================================================================================
 = Comment block for function: registerFile_getByteFrom
 = Programmer names: Tony Butterfield, Evan Phillips
 = Final test and approval date: 1/23/11
 = Synopsis:
 =      uchar registerFIle_getByteFrom(RegisterFilePtr rf, uchar reg_num, uchar which, int * error)
 =      Return value ranges: Uchar (8 bits) of data that was contained in the specified register
 =      Parameter value ranges: Pointer to register, numerical name of the register (0-15), LOB_WHICH or HOB_WHICH, pointer to an int
 =      Error conditions: rf is a null pointer, reg_num is out of range
 = Retrieves a single byte from a Register, either the high order byte or low order byte, as specified by the parameter.
 ==================================================================================================*/
uchar registerFile_getByteFrom(RegisterFilePtr rf, uchar reg_num, uchar which, int * error) {
        if (rf == NULL) {
                *error = NULL_ERR;
                return 0;
        }
        if (reg_num > REGISTER_COUNT - 1 || reg_num < 0) {
                *error = REG_NOT_FOUND_ERR;
                return 0;
        }       
        RegisterPtr regptr = rf[reg_num];
        switch (which) {
                case LOB_WHICH: {
                        return register_getLowByte(regptr, error);
                        break;
                }       
                case HOB_WHICH: {
                        return register_getHiByte(regptr, error);
                        break;
                }
                default: {
                        break;
                }
        }
        return 0; //shouldn't ever get here...
}

/*=================================================================================================
 = Comment block for function: registerFile_putByteTo
 = Programmer names: Tony Butterfield, Evan Phillips
 = Final test and approval date: 1/23/11
 = Synopsis:
 =      int registerFile_putByteTo(RegisterFilePtr rf, uchar reg_num, uchar which, uchar byte)
 =      Return value ranges: Error code: 0 (success), NULL_ERR, REG_NOT_FOUND_ERR
 =      Parameter value ranges: Pointer to register, numerical name of the register(0-15), LOB_WHICH or HOB_WHICH, data to be put in register (0 -> 2^16)
 =      Error conditions: rf is a null pointer, reg_num is out of range
 = Puts a byte of data to the specifiec Register in the RegisterFile.
 ==================================================================================================*/
int registerFile_putByteTo(RegisterFilePtr rf, uchar reg_num, uchar which, uchar byte) {
        if (rf == NULL) 
                return NULL_ERR;
        if (reg_num > REGISTER_COUNT - 1 || reg_num < 0)
                return REG_NOT_FOUND_ERR;
        RegisterPtr regptr = rf[reg_num];
        switch(which) {
                case LOB_WHICH: {
                        return register_putLowByte(regptr, byte);
                        break;
                }
                case HOB_WHICH: {
                        return register_putHiByte(regptr, byte);
                        break;
                }
                default: {
                        break;
                }
        }
        return 0; //shouldn't ever get here
}

/*=================================================================================================
 = Comment block for function: registerFile_getRegValue
 = Programmer names: Tony Butterfield, Evan Phillips
 = Final test and approval date: 1/23/11
 = Synopsis:
 =      ushort registerFile_getRegValue(RegisterFilePtr rf, uchar reg_num, int * error)
 =      Return value ranges: A ushort (16 bits) of data that was contained in the specified Register
 =      Parameter value ranges: Pointer to register, numerical name of the register (0-15), a pointer to an int
 =      Error conditions: rf is a null pointer, reg_num is out of range
 = Retrieves the entire contents of a specified Register.
 ==================================================================================================*/
ushort registerFile_getRegValue(RegisterFilePtr rf, uchar reg_num, int * error) {
        if (rf == NULL) {
                *error = NULL_ERR;
                return 0;       
        }
        if (reg_num > REGISTER_COUNT - 1 || reg_num < 0) {
                *error = REG_NOT_FOUND_ERR;
                return 0;
        }
        RegisterPtr regptr = rf[reg_num];
        return register_getRegValue(regptr, error);
}

/*=================================================================================================
 = Comment block for function: register_putRegValue
 = Programmer names: Tony Butterfield, Evan Phillips
 = Final test and approval date: 1/23/11
 = Synopsis:
 =      int registerFile_putRegValue(RegisterFilePtr rf, uchar reg_num, ushort value)
 =      Return value ranges: Error code: 0 (success), NULL_ERR, or REG_NOT_FOUND_ERR
 =      Parameter value ranges: Pointer to register, numerical name of the register (0-15), data to be put in register (0 -> 2^16)
 =      Error conditions: rf is a null pointer, reg_num is out of range
 = Puts 16 bits of data into a specified Register.
 ==================================================================================================*/
int registerFile_putRegValue(RegisterFilePtr rf, uchar reg_num, ushort value) {
        if (rf == NULL)
                return NULL_ERR;
        if (reg_num > REGISTER_COUNT - 1 || reg_num < 0)
                return REG_NOT_FOUND_ERR;
        RegisterPtr regptr = rf[reg_num];
        return register_putRegValue(regptr, value);
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 + SC-2 ALU
 + Basic Operations:	
 +		Initialize:  Creates ALU object 
 +		Execute:   Simulates a clock signal, causing the selected operation to execute.
 +
 +	'External' Operations	
 +		Load A:  Simulates the state of the A register as LdA is 
 +				enabled by explicitly loading an operand into A
 +				
 +		Load B:  Simulates the state of the B register as LdB is 
 +				enabled by explicitly loading an operand into B
 +				
 +		Select Operation:  Simulates the op_sel signal by loading a 
 +				value into the op_sel register
 +				
 +		Read Output C:  Simulates the state of the data bus as DrALU is
 +				enabled by explicitly reading the value of C				
 +
 +	'Internal' Operations:	
 +		Addition:  	C <- A + B, set status		
 +		Subtraction:  	C <- A - B, set status		
 +		Multiplication: $R8 <- (low-order) A * B, set status		
 +				$R9 <- (high-order) A * B, set status				
 +		Division: 	$RA <- (quotient) A / B, set status	
 +				$RB <- (remainder) A / B, set status				
 +		Negation:  	C <- ~A + 1 (2's complement), set status		
 +		AND:		C <- A & B, set status		
 +		OR:		C <- A | B, set status		
 +		XOR:		C <- A ^ B (bitwise XOR), set status		
 +		NOT:		C <- ~A, set status		
 +		Shift Left:	C <- A * 2, set status		
 +		Shift Right: 	C <- A / 2, set status		
 +		Set Status:  Sets the relevant bit in SW
 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/

 
// EXTERNAL FUNCTIONS - functions that simulate the ALU's interface with the rest of the CPU
 
/*=================================================================================================
 = Comment block for function: ALU_initialize()
 = Programmer names: Tony Butterfield, Evan Phillips
 = Final test and approval date: 2/12/11
 = Synopsis:
 =      ALU ALU_initialize();
 =      Return value ranges: address pointer
 =      Parameter value ranges: N/A
 =      Error conditions: N/A
 = Allocates memory and returns a pointer to the ALU struct.
 ==================================================================================================*/
ALU ALU_initialize()
{
	ALU alu = (ALU) malloc(sizeof(ALUStr));
	
	alu->A = register_initialize();
	alu->B = register_initialize();
	alu->C = register_initialize();
	alu->op_sel = 0;
	
	return alu;
} 
 
/*=================================================================================================
 = Comment block for function: ALU_execute()
 = Programmer names: Tony Butterfield, Evan Phillips
 = Final test and approval date: 
 = Synopsis:
 =      int ALU_execute(ALU, RegisterFilePtr, RegisterPtr);
 =      Return value ranges: 0 (success), NULL_ERR
 =      Parameter value ranges: pointers to ALU, CPU registerfile, SW register				
 =      Error conditions: Null pointers
 = Simulates a clock signal, causing the selected operation to execute.
 ==================================================================================================*/
int ALU_execute(ALU aa, RegisterFilePtr rf, RegisterPtr sw)
{
	int err = 0;
	int* error = &err;
	
	if(aa != NULL && rf != NULL && sw != NULL && error != NULL)
	{
		// switch to select internal operation
		switch(aa->op_sel)
		{
			case OP_ADD:
				err = ALU_add(aa);
				break;
			case OP_SUB:
				err = ALU_sub(aa);
				break;
			case OP_MUL:
				err = ALU_multiply(aa, rf);
				break;
			case OP_DIV:	
				err = ALU_divide(aa, rf);
				break;
			case OP_NEG:
				err = ALU_negate(aa);
				break;
			case OP_AND:
				err = ALU_and(aa);
				break;
			case OP_OR:	
				err = ALU_or(aa);
				break;
			case OP_XOR:
				err = ALU_xor(aa);
				break;
			case OP_NOT:
				err = ALU_not(aa);
				break;
			case OP_SHL:
				err = ALU_shiftLeft(aa);
				break;
			case OP_SHR:
				err = ALU_shiftRight(aa);
				break;				
			default:
				break;		
		}
		
		err = ALU_setStatusWord(aa, rf, sw);
	}
	else
	{
		err = NULL_ERR;	
	}
	
	aa->op_sel = 0x00;
	
	return err;
} 
 
/*=================================================================================================
 = Comment block for function: ALU_loadA()
 = Programmer names: Tony Butterfield, Evan Phillips
 = Final test and approval date: 2/12/11
 = Synopsis:
 =      int ALU_loadA(ALU, ushort);
 =      Return value ranges: 0 (success), NULL_ERR
 =      Parameter value ranges: pointers to ALU and 16-bit unsigned data value
 =      Error conditions: ALU pointer is null.
 = Simulates the state of the A register as LdA is enabled by explicitly loading an operand into A
 ==================================================================================================*/
int ALU_loadA(ALU aa, ushort input)
{
	int err = 0;
	int* error = &err;
	
	if(aa != NULL)
	{
		err = register_putRegValue(aa->A, input);
	}	
	else
	{
		err = NULL_ERR;	
	}
	return err;
}

/*=================================================================================================
 = Comment block for function: ALU_loadB()
 = Programmer names: Tony Butterfield, Evan Phillips
 = Final test and approval date: 2/12/11
 = Synopsis:
 =      int ALU_loadB(ALU, ushort);
 =      Return value ranges: 0 (success), NULL_ERR
 =      Parameter value ranges: pointers to ALU and 16-bit unsigned data value
 =      Error conditions: ALU pointer is null.
 = Simulates the state of the B register as LdB is enabled by explicitly loading an operand into B
 ==================================================================================================*/
int ALU_loadB(ALU aa, ushort input)
{
	int err = 0;
	int* error = &err;
	
	if(aa != NULL)
	{
		err = register_putRegValue(aa->B, input);
	}
	else
	{
		err = NULL_ERR;	
	}
	return err;
}

/*=================================================================================================
 = Comment block for function: ALU_selectOperation()
 = Programmer names: Tony Butterfield, Evan Phillips
 = Final test and approval date: 2/12/11
 = Synopsis:
 =      int ALU_selectOperation(ALU, uchar)
 =      Return value ranges: 0 (success), NULL_ERR, OP_OUT_OF_RANGE_ERR
 =      Parameter value ranges: pointers to ALU and 8-bit unsigned data value
 =      Error conditions: ALU pointer is null or op is out of range of the defined operations.
 = Simulates the op_sel signal by loading a value into the op_sel register
 ==================================================================================================*/
int ALU_selectOperation(ALU aa, uchar op)
{
	int err = 0;
	int* error = &err;
	
	if(aa != NULL && op <= OP_SHR) 
	{
		aa->op_sel = op;
	}
	else if (aa == NULL) 
	{
		err = NULL_ERR;	
	}
	else 
	{
		err = OP_OUT_OF_RANGE_ERR;
	}
	return err;
}

/*=================================================================================================
 = Comment block for function: ALU_setStatusWord()
 = Programmer names: Tony Butterfield, Evan Phillips
 = Final test and approval date: 
 = Synopsis:
 =      int ALU_setStatusWord(ALU, RegisterFilePtr, RegisterPtr)
 =      Return value ranges: 0 (success), NULL_ERR
 =      Parameter value ranges: pointers to ALU, register file, and SW register
 =      Error conditions: null pointers.
 = Set the SW status bits to the current status value
 ==================================================================================================*/
int ALU_setStatusWord(ALU aa, RegisterFilePtr rf, RegisterPtr sw)
{
	int err = 0;
	int* error = &err;
	
	if(aa != NULL && sw != NULL)
	{
		ushort regA = register_getRegValue(aa->A, error);		// fetch values of registers
		ushort regB = register_getRegValue(aa->B, error);
		ushort regC = register_getRegValue(aa->C, error);
		
		ushort multHigh = registerFile_getRegValue(rf, R9, error);
		ushort multLow = registerFile_getRegValue(rf, R8, error);
		ushort divResult = registerFile_getRegValue(rf, RA, error);
		ushort divRemain = registerFile_getRegValue(rf, RB, error);
		
		ushort regSW = register_getRegValue(sw, error) & SW_CLEAR_STATUS;		
		
		ushort signA = regA & BIT_15;					// determine sign-bits
		ushort signB = regB & BIT_15;
		ushort signC = regC & BIT_15;
		ushort signMult = multHigh & BIT_15;
		ushort signDivRes = divResult & BIT_15;
		ushort signDivRem = divRemain & BIT_15;
		
		
		switch(aa->op_sel)
		{
			case OP_ADD:
				// set N bit
				if(signC == BIT_15)
				{
					regSW = regSW | SW_N_BIT;
				}
				// set Z bit
				if(regC == 0)
				{
					regSW = regSW | SW_Z_BIT;
				}
				// set C bit when (A + B > 0xFFFF)
				if(regA + regB > MAX_USHORT)
				{
					regSW = regSW | SW_C_BIT;
				}
				// set O bit when (+ ADD + = -) or (- ADD - = +)
				if(signA == signB && signA != signC)
				{
					regSW = regSW | SW_O_BIT;
				}
				break;
			case OP_SUB:
				// set N bit
				if(signC == BIT_15)
				{
					regSW = regSW | SW_N_BIT;
				}
				// set Z bit
				if(regC == 0)
				{
					regSW = regSW | SW_Z_BIT;
				}				
				// set C bit when (A - B > 0xFFFF)
				if(regA - regB > MAX_USHORT)
				{
					regSW = regSW | SW_C_BIT;
				}
				// set O bit when (+ SUB - = -) or (- SUB + = +)
				if(signA != signB && signA != signC)
				{
					regSW = regSW | SW_O_BIT;
				}
				break;
			case OP_MUL:
				// set N bit
				if(signMult == BIT_15)
				{
					regSW = regSW | SW_N_BIT;
				}
				// set Z bit
				if(multHigh == 0 && multLow == 0)
				{
					regSW = regSW | SW_Z_BIT;
				}
				// set C bit
				// set O bit when (+ MUL + = -) or (- MUL - = -)
				if(signA == signB && signA != signC)
				{
					regSW = regSW | SW_O_BIT;
				}
				break;
			case OP_DIV:
				// set N bit
				if(signDivRes == BIT_15 || signDivRem == BIT_15)
				{
					regSW = regSW | SW_N_BIT;
				}
				// set Z bit
				if(divResult == 0 && divRemain == 0)
				{
					regSW = regSW | SW_Z_BIT;
				}
				// set C bit
				// set O bit
				break;
			case OP_NEG:
				// set N bit
				if(signC == BIT_15)
				{
					regSW = regSW | SW_N_BIT;
				}
				// set Z bit
				if(regC == 0)
				{
					regSW = regSW | SW_Z_BIT;
				}
				// set C bit
				// set O bit
				break;
			case OP_AND:
				// set N bit
				if(signC == BIT_15)
				{
					regSW = regSW | SW_N_BIT;
				}
				// set Z bit
				if(regC == 0)
				{
					regSW = regSW | SW_Z_BIT;
				}
				// set C bit
				// set O bit
				break;
			case OP_OR:
				// set N bit
				if(signC == BIT_15)
				{
					regSW = regSW | SW_N_BIT;
				}
				// set Z bit
				if(regC == 0)
				{
					regSW = regSW | SW_Z_BIT;
				}
				// set C bit
				// set O bit
				break;
			case OP_XOR:
				// set N bit
				if(signC == BIT_15)
				{
					regSW = regSW | SW_N_BIT;
				}
				// set Z bit
				if(regC == 0)
				{
					regSW = regSW | SW_Z_BIT;
				}
				// set C bit
				// set O bit
				break;
			case OP_NOT:
				// set N bit
				if(signC == BIT_15)
				{
					regSW = regSW | SW_N_BIT;
				}
				// set Z bit
				if(regC == 0)
				{
					regSW = regSW | SW_Z_BIT;
				}
				// set C bit
				// set O bit
				break;
			case OP_SHL:
				// set N bit
				if(signC == BIT_15)
				{
					regSW = regSW | SW_N_BIT;
				}
				// set Z bit
				if(regC == 0)
				{
					regSW = regSW | SW_Z_BIT;
				}
				// set C bit
				if(signA == BIT_15)
				{
					regSW = regSW | SW_C_BIT;
				}				
				// set O bit
				break;
			case OP_SHR:
				// set N bit
				if(signC == BIT_15)
				{
					regSW = regSW | SW_N_BIT;
				}
				// set Z bit
				if(regC == 0)
				{
					regSW = regSW | SW_Z_BIT;
				}
				// set C bit
				if(regA & 0x0001 == 0x0001)
				{
					regSW = regSW | SW_C_BIT;
				}
				// set O bit
				break;
			default:
				break;
		}
		
		err = register_putRegValue(sw, regSW);	// update with new status
	}
	else
	{
		err = NULL_ERR;	
	}
	return err;
}

/*=================================================================================================
 = Comment block for function: ALU_readC()
 = Programmer names: Tony Butterfield, Evan Phillips
 = Final test and approval date: 2/12/11
 = Synopsis:
 =      ushort	ALU_readC(ALU, int*);			
 =      Return value ranges: 16-bit unsigned data value
 =      Parameter value ranges: pointers to ALU and pointer to int for 'read' 
				function error codes.
 =      Error conditions: null pointers.
 = Simulates the state of the data bus as DrALU is enabled by explicitly reading the value of C
 ==================================================================================================*/
ushort ALU_readC(ALU aa, int* error)			
{	
	ushort result = 0;
	if(aa != NULL && error != NULL)
	{
		result = register_getRegValue(aa->C, error);	
	}
	else
	{
		*error = NULL_ERR;	
	}
	return result;
}

// INTERNAL FUNCTIONS - functions that simulate the ALU's internal operations

/*=================================================================================================
 = Comment block for function: ALU_add()
 = Programmer names: Tony Butterfield, Evan Phillips
 = Final test and approval date: 2/12/11
 = Synopsis:
 =      int	ALU_add(ALU);			
 =      Return value ranges: 0 (success), NULL_ERR
 =      Parameter value ranges: pointers to ALU
 =      Error conditions: null pointer.
 = execute operation A + B
 ==================================================================================================*/
int ALU_add(ALU aa)
{
	int err = 0;
	int* error = &err;
	
	if(aa != NULL)
	{
		ushort regA = register_getRegValue(aa->A, error);	// fetch values of registers
		ushort regB = register_getRegValue(aa->B, error);
		
		err = register_putRegValue(aa->C, regA + regB);
	}
	else
	{
		err = NULL_ERR;
	}	
	
	return err;
}
 
/*=================================================================================================
 = Comment block for function: ALU_sub()
 = Programmer names: Tony Butterfield, Evan Phillips
 = Final test and approval date: 2/12/11
 = Synopsis:
 =      int	ALU_sub(ALU);			
 =      Return value ranges: 0 (success), NULL_ERR
 =      Parameter value ranges: pointer to ALU
 =      Error conditions: null pointer.
 = execute operation A - B
 ==================================================================================================*/
int ALU_sub(ALU aa)
{
	int err = 0;
	int* error = &err;
	
	if(aa != NULL)
	{
		ushort regA = register_getRegValue(aa->A, error);	// fetch values of registers
		ushort regB = register_getRegValue(aa->B, error);
		
		err = register_putRegValue(aa->C, regA - regB);
	}
	else
	{
		err = NULL_ERR;
	}	
	return err;
}
 
/*=================================================================================================
 = Comment block for function: ALU_multiply()
 = Programmer names: Tony Butterfield, Evan Phillips
 = Final test and approval date: 2/12/11
 = Synopsis:
 =      int	ALU_multiply(ALU, RegisterFilePtr);			
 =      Return value ranges: 0 (success), NULL_ERR
 =      Parameter value ranges: pointers to ALU and reg_file		
 =      Error conditions: null pointers.
 = execute operation A * B
 ==================================================================================================*/
int ALU_multiply(ALU aa, RegisterFilePtr rf)
{
	int err = 0;
	int* error = &err;
	
	if(aa != NULL && rf != NULL)
	{
		ushort regA = register_getRegValue(aa->A, error);	// fetch values of registers
		ushort regB = register_getRegValue(aa->B, error);
		
		int tmp = regA * regB;
		
		err = registerFile_putRegValue(rf, R8, tmp & LOW_MASK);
		err = registerFile_putRegValue(rf, R9, tmp & HOW_MASK);
	}
	else
	{
		err = NULL_ERR;
	}	
	return err;
}
 
/*=================================================================================================
 = Comment block for function: ALU_divide()
 = Programmer names: Tony Butterfield, Evan Phillips
 = Final test and approval date: 2/12/11
 = Synopsis:
 =      int	ALU_divide(ALU, RegisterFilePtr);			
 =      Return value ranges: 0 (success), NULL_ERR, ALU_DIVIDE_BY_ZERO
 =      Parameter value ranges: pointers to ALU, reg_file
 =      Error conditions: null pointers, Divide-by-Zero.
 = execute operation A / B
 ==================================================================================================*/
int ALU_divide(ALU aa, RegisterFilePtr rf)
{
	int err = 0;
	int* error = &err;
	
	if(aa != NULL && rf != NULL)
	{
		ushort regA = register_getRegValue(aa->A, error);	// fetch values of registers
		ushort regB = register_getRegValue(aa->B, error);
		
		if(regB != 0x00)
		{		
			ushort res = regA / regB;
			ushort rem = regA % regB;

			err = registerFile_putRegValue(rf, RA, res);
			err = registerFile_putRegValue(rf, RB, rem);
		}
		else
		{
			err = ALU_DIVIDE_BY_ZERO;
		}
	}
	else
	{
		err = NULL_ERR;
	}	
	return err;
} 

/*=================================================================================================
 = Comment block for function: ALU_negate()
 = Programmer names: Tony Butterfield, Evan Phillips
 = Final test and approval date: 2/12/11
 = Synopsis:
 =      int	ALU_negate(ALU);			
 =      Return value ranges: 0 (success), NULL_ERR
 =      Parameter value ranges: pointers to ALU
 =      Error conditions: null pointer.
 = execute operation !A + 1
 ==================================================================================================*/
int ALU_negate(ALU aa)
{
	int err = 0;
	int* error = &err;
	
	if(aa != NULL)
	{
		ushort regA = register_getRegValue(aa->A, error);	// fetch values of register		
		
		err = register_putRegValue(aa->C, ~regA + 1);
	}
	else
	{
		err = NULL_ERR;
	}	
	return err;
} 

/*=================================================================================================
 = Comment block for function: ALU_and()
 = Programmer names: Tony Butterfield, Evan Phillips
 = Final test and approval date: 2/12/11
 = Synopsis:
 =      int	ALU_and(ALU);			
 =      Return value ranges: 0 (success), NULL_ERR
 =      Parameter value ranges: pointer to ALU
 =      Error conditions: null pointer.
 = execute operation A & B
 ==================================================================================================*/
int ALU_and(ALU aa)
{
	int err = 0;
	int* error = &err;
	
	if(aa != NULL)
	{
		ushort regA = register_getRegValue(aa->A, error);	// fetch values of registers
		ushort regB = register_getRegValue(aa->B, error);
		
		err = register_putRegValue(aa->C, regA & regB);
	}
	else
	{
		err = NULL_ERR;
	}	
	return err;
} 
 
/*=================================================================================================
 = Comment block for function: ALU_or()
 = Programmer names: Tony Butterfield, Evan Phillips
 = Final test and approval date: 2/12/11
 = Synopsis:
 =      int	ALU_or(ALU);			
 =      Return value ranges: 0 (success), NULL_ERR
 =      Parameter value ranges: pointer to ALU
 =      Error conditions: null pointer.
 = execute operation A | B
 ==================================================================================================*/

int ALU_or(ALU aa)
{
	int err = 0;
	int* error = &err;
	
	if(aa != NULL)
	{
		ushort regA = register_getRegValue(aa->A, error);	// fetch values of registers
		ushort regB = register_getRegValue(aa->B, error);
		
		err = register_putRegValue(aa->C, regA | regB);
	}
	else
	{
		err = NULL_ERR;
	}	
	return err;
} 
 
/*=================================================================================================
 = Comment block for function: ALU_xor()
 = Programmer names: Tony Butterfield, Evan Phillips
 = Final test and approval date: 2/12/11
 = Synopsis:
 =      int	ALU_xor(ALU);			
 =      Return value ranges: 0 (success), NULL_ERR
 =      Parameter value ranges: pointer to ALU 
 =      Error conditions: null pointer.
 = execute operation A XOR B
 ==================================================================================================*/
int ALU_xor(ALU aa)
{
	int err = 0;
	int* error = &err;
	
	if(aa != NULL)
	{
		ushort regA = register_getRegValue(aa->A, error);	// fetch values of registers
		ushort regB = register_getRegValue(aa->B, error);
		
		err = register_putRegValue(aa->C, regA ^ regB);
	}
	else
	{
		err = NULL_ERR;
	}	
	return err;
}

/*=================================================================================================
 = Comment block for function: ALU_not()
 = Programmer names: Tony Butterfield, Evan Phillips
 = Final test and approval date: 2/12/11
 = Synopsis:
 =      int	ALU_not(ALU);			
 =      Return value ranges: 0 (success), NULL_ERR
 =      Parameter value ranges: pointer to ALU
 =      Error conditions: null pointer.
 = execute operation !A
 ==================================================================================================*/
int ALU_not(ALU aa)
{
	int err = 0;
	int* error = &err;
	
	if(aa != NULL)
	{
		ushort regA = register_getRegValue(aa->A, error);	// fetch values of register
		
		err = register_putRegValue(aa->C, ~regA);
	}
	else
	{
		err = NULL_ERR;
	}	
	return err;
}

/*=================================================================================================
 = Comment block for function: ALU_shiftLeft()
 = Programmer names: Tony Butterfield, Evan Phillips
 = Final test and approval date: 2/12/11
 = Synopsis:
 =      int	ALU_shiftLeft(ALU);			
 =      Return value ranges: 0 (success), NULL_ERR
 =      Parameter value ranges: pointer to ALU
 =      Error conditions: null pointer.
 = execute operation bit shift left
 ==================================================================================================*/
int ALU_shiftLeft(ALU aa)
{
	int err = 0;
	int* error = &err;
	
	if(aa != NULL)
	{
		ushort regA = register_getRegValue(aa->A, error);	// fetch values of register
		
		*aa->C = *aa->A << 1;  
		err = register_putRegValue(aa->C, regA << 1);
	}
	else
	{
		err = NULL_ERR;
	}	
	return err;
}

/*=================================================================================================
 = Comment block for function: ALU_shiftRight()
 = Programmer names: Tony Butterfield, Evan Phillips
 = Final test and approval date: 2/12/11
 = Synopsis:
 =      int	ALU_shiftRight(ALU);			
 =      Return value ranges: 0 (success), NULL_ERR
 =      Parameter value ranges: pointers to ALU
 =      Error conditions: null pointers.
 = execute operation A >> C
 ==================================================================================================*/

int ALU_shiftRight(ALU aa)
{
	int err = 0;
	int* error = &err;
	
	if(aa != NULL)
	{
		ushort regA = register_getRegValue(aa->A, error);	// fetch values of register
		
		err = register_putRegValue(aa->C, regA >> 1);
	}
	else
	{
		err = NULL_ERR;
	}	
	return err;
}

/*=================================================================================================
 = Comment block for function: ALUTest
 = Programmer names: Tony Butterfield, Evan Phillips
 = Final test and approval date: 2/12/11
 = Synopsis:
 =      void ALUTest()			
 =      Return value ranges: N/A
 =      Parameter value ranges: N/A
 =      Error conditions: N/A
 = Tests most functions of the ALU. Some functions are not explicitly tested here because they
 = are tested by manually inspecting the output of the test driver.
 ==================================================================================================*/
void ALUTest() {
	//ALU_initialize()
	ALU a = ALU_initialize();
	if (a == NULL) 
		printf("ALU_initialize returned null");
	if (a->A == NULL || a->B == NULL || a->C == NULL)
		printf("ALU_initialze left internal register null");
	
	//ALU_loadA
	ushort ushort_val = 32;
	int err = 0;
	int * error = &err;
	err = ALU_loadA(NULL, ushort_val);
	if (err != NULL_ERR)
		printf("ALU_loadA allowed null ALU");
	err = ALU_loadA(a, ushort_val);
	if (*a->A != ushort_val) 
		printf("ALU_loadA loaded incorrect value. Expected: %X Got: %X", ushort_val, *a->A);
	
	//ALU_loadB
	err = ALU_loadB(NULL, ushort_val);
	if (err != NULL_ERR)
		printf("ALU_loadB allowed null ALU");
	err = ALU_loadB(a, ushort_val);
	if (*a->B != ushort_val) 
		printf("ALU_loadB loaded incorrect value. Expected: %X Got: %X", ushort_val, *a->B);
	
	//ALU_selectOperation
	uchar uchar_val = OP_MUL;
	err = ALU_selectOperation(NULL, uchar_val);
	if (err != NULL_ERR)
		printf("ALU_selectOperation allowed null ALU");
	err = ALU_selectOperation(a, 0xFF);
	if (err != OP_OUT_OF_RANGE_ERR)
		printf("ALU_selectOperation allowed out of range operation");
	err = ALU_selectOperation(a, uchar_val);
	if (a->op_sel != uchar_val)
		printf("ALU_selectOperation did not set correct operation. Expected: %X Got: %X", uchar_val, a->op_sel);
	
	//ALU_readC
	*a->C = ushort_val;
	ushort ushort_result = 0;
	ushort_result = ALU_readC(a, error);
	if (ushort_result != ushort_val)
		printf("ALU_readC returned incorrect value. Expected: %X Got: %X", ushort_val, ushort_result);
	
	//set up for testing the arithemtic functions
	ushort regA = 20;
	ushort regB = 11;
	ALU_loadA(a, regA);
	ALU_loadB(a, regB);
	RegisterFilePtr rf = registerFile_initialize();
	RegisterPtr sw = register_initialize();

	//ALU_add
	err = ALU_add(NULL);
	if (err != NULL_ERR)
		printf("ALU_add allowed null parameter");
	err = ALU_add(a);
	if (*a->C != (regA + regB))
		printf("ALU_add set wrong value. Expected: %X Got: %X", regA + regB, *a->C);
	
	//ALU_sub
	err = ALU_sub(NULL);
	if (err != NULL_ERR)
		printf("ALU_sub allowed null parameter");
	err = ALU_sub(a);
	if (*a->C != (regA - regB))
		printf("ALU_sub set wrong value. Expected: %X Got: %X", regA - regB, *a->C);
	
	//ALU_mul
	err = ALU_multiply(NULL, rf);
	if (err != NULL_ERR)
		printf("ALU_multiply allowed null parameter");
	err = ALU_multiply(a, NULL);
	if (err != NULL_ERR)
		printf("ALU_multiply allowed null parameter");
	err = ALU_multiply(a, rf);
	int int_result = (*rf[9] << 16) + *rf[8];
	if (int_result != (regA * regB))
		printf("ALU_multiply set wrong value. Expected: %X Got: %X", regA * regB, int_result);
	
	//ALU_divide
	err = ALU_divide(NULL, rf);
	if (err != NULL_ERR)
		printf("ALU_divide allowed null parameter");
	err = ALU_divide(a, NULL);
	if (err != NULL_ERR)
		printf("ALU_divide allowed null parameter");
	err = ALU_divide(a, rf);
	ushort_result = *rf[0xA];
	if (ushort_result != (ushort)(regA / regB))
		printf("ALU_divide set wrong value. Expected: %X Got: %X", regA / regB, ushort_result);
	ushort remainder = *rf[0xB];
	if (remainder != regA % regB)
		printf("ALU_divide set wrong remainder. Expected: %X Got: %X", regA % regB, remainder);

	//ALU_negate
	err = ALU_negate(NULL);
	if (err != NULL_ERR)
		printf("ALU_negate allowed null parameter");
	err = ALU_negate(a);
	if (*a->C != (ushort)-regA)
		printf("ALU_negate set wrong value. Expected: %X Got: %X", -regA, *a->C);

	//ALU_and
	err = ALU_and(NULL);
	if (err != NULL_ERR)
		printf("ALU_and allowed null parameter");
	err = ALU_and(a);
	if (*a->C != (regA & regB))
		printf("ALU_and set wrong value. Expected: %X Got: %X", regA & regB, *a->C);
	
	//ALU_or
	err = ALU_or(NULL);
	if (err != NULL_ERR)
		printf("ALU_or allowed null parameter");
	err = ALU_or(a);
	if (*a->C != (regA | regB))
		printf("ALU_or set wrong value. Expected: %X Got: %X", regA | regB, *a->C);

	//ALU_xor
	err = ALU_xor(NULL);
	if (err != NULL_ERR)
		printf("ALU_xor allowed null parameter");
	err = ALU_xor(a);
	if (*a->C != (regA ^ regB))
		printf("ALU_xor set wrong value. Expected: %X Got: %X", regA ^ regB, *a->C);
	
	//ALU_not
	err = ALU_not(NULL);
	if (err != NULL_ERR)
		printf("ALU_not allowed null parameter");
	err = ALU_not(a);
	if (*a->C != (ushort)~regA)
		printf("ALU_not set wrong value. Expected: %X Got: %X", ~regA, *a->C);
	
	//ALU_shiftLeft
	err = ALU_shiftLeft(NULL);
	if (err != NULL_ERR)
		printf("ALU_shiftLeft allowed null parameter");
	err = ALU_shiftLeft(a);
	if (*a->C != (regA << 1))
		printf("ALU_shiftLeft set wrong value. Expected: %X Got: %X", regA << 1, *a->C);

	//ALU_shiftRight
	err = ALU_shiftRight(NULL);
	if (err != NULL_ERR)
		printf("ALU_shiftRight allowed null parameter");
	err = ALU_shiftRight(a);
	if (*a->C != (regA >> 1))
		printf("ALU_shiftRight set wrong value. Expected: %X Got: %X", regA >> 1, *a->C);

}

// TEST DRIVER HELPER FUNCTIONS - functions called from main to simulate the cpu environment

/*=================================================================================================
 = 	Comment block for function: test_singleOp()
 = 	Programmer names: Tony Butterfield, Evan Phillips
 = 	Final test and approval date: 
 =
 = 		Synopsis:		int test_singleOp(ALU, RegisterFilePtr)
 =		Return value ranges: 	0 (success), NULL_ERR, OP_OUT_OF_RANGE_ERR
 =		Parameter value ranges: pointer to ALU and Register File,
 =		Error conditions: 	null pointer or invalid op
 =
 = 	prompts user to select a register to act as source and destination, and outputs dummy 
 =	assem code for selected op.
 =
 ==================================================================================================*/
 
int test_singleOp(ALU aa, RegisterFilePtr rf, RegisterPtr sw)
{	
	int err = 0;
	int* error = &err;
	
	if(aa != NULL && rf != NULL)
	{	
		uchar reg_A;
		uchar op_sel;
		printf("\nFor this operation, select a single register to act as source and destination.\n");
		
			
		BOOLEAN loop_A;
		do				// get register 'A'
		{
			printf("Select Register ($R0-$RF) :  $R");
			int temp;
			scanf("%3X", &temp);
			reg_A = temp;
			
			if(reg_A >= REGISTER_COUNT || reg_A < 0)
			{
				printf("Invalid register input.  Please try again.\n");
				loop_A = TRUE;
			}
			
		} while(loop_A == TRUE);		
		
		op_sel = test_getOpSel(aa, error);	// get operation selected		
		switch (op_sel)
		{
			case OP_NEG:
				printf("\nNEG $R%X		;-($R%X)\n", reg_A, reg_A);
				break;
			case OP_NOT:
				printf("\nNOT $R%X		;!($R%X)\n", reg_A, reg_A);
				break;			
			default:
				err = OP_OUT_OF_RANGE_ERR;
				break;
		}
		
		err = ALU_loadA(aa, registerFile_getRegValue(rf, reg_A, error));	// load ALU reg A
		err = ALU_execute(aa, rf, sw);			//execute selected op and set status word		
		err = registerFile_putRegValue(rf, reg_A, ALU_readC(aa, error));	// store result to reg A
		
	}
	else
	{
		err = NULL_ERR;
	}	
	
	return err;
}
 

/*=================================================================================================
 = 	Comment block for function: test_doubleOp()
 = 	Programmer names: Tony Butterfield, Evan Phillips
 = 	Final test and approval date: 
 =
 = 		Synopsis:		int test_doubleOp(ALU, RegisterFilePtr)
 =		Return value ranges: 	0 (success), NULL_ERR, OP_OUT_OF_RANGE_ERR
 =		Parameter value ranges: pointer to ALU and Register File,
 =		Error conditions: 	null pointer or invalid op
 =
 = 	prompts user to select registers to act as source and destination, and outputs dummy 
 =	assem code for selected op.
 =
 ==================================================================================================*/
 
int test_doubleOp(ALU aa, RegisterFilePtr rf, RegisterPtr sw)
{	
	int err = 0;
	int* error = &err;
	
	if(aa != NULL && rf != NULL)
	{	
		uchar reg_A;
		uchar reg_B;
		uchar op_sel;
		
		printf("\nFor this operation, select an 'A' register to act as source 1 and destination,\n");
		printf("and a 'B' register to act as source 2.\n\n");
		
		BOOLEAN loop_A;
		do				// get register 'A'
		{
			printf("Select Register 'A' ($R0-$RF) :  $R");
			int temp;
			scanf("%3X", &temp);
			reg_A = (uchar)temp;
			
			if(reg_A >= REGISTER_COUNT)
			{
				printf("Invalid register input.  Please try again.\n");
				loop_A = TRUE;
			}
			
		} while(loop_A == TRUE);			
		
		BOOLEAN loop_B;
		do				// get register 'B'
		{
			printf("Select Register 'B' ($R0-$RF) :  $R");
			int temp;
			scanf("%3X", &temp);
			reg_B = (uchar)temp;
			
			if(reg_B >= REGISTER_COUNT)
			{
				printf("Invalid register input.  Please try again.\n");
				loop_B = TRUE;
			}
			
		} while(loop_B == TRUE);		
		
		op_sel = test_getOpSel(aa, error);	// get operation selected		
		switch (op_sel)				// print assem command
		{
			case OP_ADD:
				printf("\nADD $R%X, $R%X	;$R%X <- $R%X + $R%X\n",
					reg_A, reg_B, reg_A, reg_A, reg_B);
				break;
			case OP_SUB:
				printf("\nSUB $R%X, $R%X	;$R%X <- $R%X - $R%X\n",
					reg_A, reg_B, reg_A, reg_A, reg_B);
				break;	
			case OP_MUL:
				printf("\nMUL $R%X, $R%X	;$R9(HI) $R8(LO) <- $R%X * $R%X\n",
					reg_A, reg_B, reg_A, reg_B);
				break;
			case OP_DIV:
				printf("\nDIV $R%X, $R%X	;$RA(Result) $RB(Remain) <- $R%X / $R%X\n",
					reg_A, reg_B, reg_A, reg_B);
				break;
			case OP_AND:
				printf("\nAND $R%X, $R%X	;$R%X <- $R%X & $R%X\n",
					reg_A, reg_B, reg_A, reg_A, reg_B);
				break;
			case OP_OR:
				printf("\nOR $R%X, $R%X	;$R%X <- $R%X | $R%X\n",
					reg_A, reg_B, reg_A, reg_A, reg_B);
				break;
			case OP_XOR:
				printf("\nXOR $R%X, $R%X	;$R%X <- $R%X ^ $R%X\n",
					reg_A, reg_B, reg_A, reg_A, reg_B);
				break;
			case OP_SHL:
				printf("\nSHL $R%X, $R%X	;$R%X <- $R%X * 2\n",
					reg_A, reg_B, reg_A,reg_B);
				break;	
			case OP_SHR:
				printf("\nSHR $R%X, $R%X	;$R%X <- $R%X / 2\n",
					reg_A, reg_B, reg_A,reg_B);
				break;	
			default:
				err = OP_OUT_OF_RANGE_ERR;
				break;
		}
		
		
		ALU_loadA(aa, registerFile_getRegValue(rf, reg_A, error));	// load ALU reg A
		ALU_loadB(aa, registerFile_getRegValue(rf, reg_B, error));	// load ALU reg B		
		
		err = ALU_execute(aa, rf, sw);			//execute selected op and set status word
								//if div-by-zero, need to recover that err
								
		if(op_sel != OP_MUL && op_sel != OP_DIV)	// store result to reg A (unless MUL or DIV
		{		
			registerFile_putRegValue(rf, reg_A, ALU_readC(aa, error));	
		}
		
	}
	else
	{
		err = NULL_ERR;
	}	
	
	return err;
} 
 
/*=================================================================================================
 = 	Comment block for function: display_registerFile()
 = 	Programmer names: Tony Butterfield, Evan Phillips
 = 	Final test and approval date: 1/24/11
 =
 = 		Synopsis:		int display_registerFile(RegisterFilePtr)
 =		Return value ranges: 	0 (success), NULL_ERR
 =		Parameter value ranges: pointer to Register File
 =		Error conditions: 	null pointer
 =
 = 	Displays a snapshot of the register file.
 =
 ==================================================================================================*/
int display_registerFile(RegisterFilePtr rf)
{
	int err = 0;
	int* error = &err;
	
	int i;
	if (rf == NULL) {
		err = NULL_ERR;
		return;
	}
	printf("\nSnapshot of Register File:\n");
	for(i = 0; i < REGISTER_COUNT/2; i++)
	{
		printf("     $R%X:  0x%04X     $R%X:  0x%04X\n", 
			i, registerFile_getRegValue(rf, i, error), i + (REGISTER_COUNT / 2), 
			registerFile_getRegValue(rf, i + (REGISTER_COUNT / 2), error));
	}
	return err;
}


/*=================================================================================================
 = 	Comment block for function: display_statusBits()
 = 	Programmer names: Tony Butterfield, Evan Phillips
 = 	Final test and approval date: 
 =
 = 		Synopsis:		int display_statusBits(RegisterPtr)
 =		Return value ranges: 	0 (success), NULL_ERR
 =		Parameter value ranges: pointer to status word register.
 =		Error conditions: 	null pointers
 =
 = 	displays snapshot of the SW's status bits.
 =
 ==================================================================================================*/
int display_statusBits(RegisterPtr sw)
{
	int err = 0;
	int* error = &err;
	
	if(sw != NULL)
	{
		ushort n = (register_getRegValue(sw, error) & SW_N_BIT) / SW_N_BIT;
		ushort z = (register_getRegValue(sw, error) & SW_Z_BIT) / SW_Z_BIT;
		ushort c = (register_getRegValue(sw, error) & SW_C_BIT) / SW_C_BIT;
		ushort o = (register_getRegValue(sw, error) & SW_O_BIT) / SW_O_BIT;
		
		printf("\nStatus Bits:  N = %X, Z = %X, C = %X, O = %X\n\n", n, z, c, o);
	}
	else
	{
		err = NULL_ERR;
	}
	
	return err;
}

/*=================================================================================================
 = 	Comment block for function: test_getOpSel()
 = 	Programmer names: Tony Butterfield, Evan Phillips
 = 	Final test and approval date: 
 =
 = 		Synopsis:		int test_getOpSel(ALU, int*)
 =		Return value ranges: 	0xFF on failure, or  hex code for selected operation
 =		Parameter value ranges: pointers to ALU	and pointer to int for 'read' function error codes.
 =		Error conditions: 	null pointers
 =
 = 	retrieves the value of the alu's op_sel entry for use in the test driver.
 =
 ==================================================================================================*/
uchar test_getOpSel(ALU aa, int* error)
{
	uchar op = 0xFF;
	
	if(aa != NULL && error != NULL)
	{
		op = aa->op_sel;	
	}
	else
	{
		*error = NULL_ERR;	
	}
	
	return op;
}
 
 
/*=================================================================================================
 = 	Comment block for function: main()
 = 	Programmer names: Tony Butterfield, Evan Phillips
 = 	Final test and approval date: 
 =
 = 		Synopsis:		int main()
 =		Return value ranges: 	0 (success)
 =		Parameter value ranges: N/A
 =		Error conditions: 	N/A
 =
 = 	the ALU test driver.
 =
 ==================================================================================================*/
 
int main()
{
	ALUTest();					// ALU unit tests	
	
	int err = 0;
	int* error = &err;	
	
	
	// Test Driver bullet points from Assignment page:
	
		// Intialize a register file and ALU object 
		// Store specific values in two or more registers 
		// Display the register file 
		// Move data from one or more registers to registers A and B, as needed 
		// Print the operation to be performed in the format of an assembly line, e.g. ADD $R1, $R2. 
		// Perform the indicated operation 
		// Store ALU register R back to the destination register 
		//		(observe the rule that the destination register is the same as the first operand) 
		// Display the register file after each operation 
	
	
	//*********************************************************************
	// Initialize a register file and ALU object
	//*********************************************************************
	
	printf("Initializing ALU and supporting registers:  ");
	
	RegisterFilePtr rf = registerFile_initialize();	// initialize register file
	RegisterPtr sw = register_initialize();		// initialize CPU Status Word register required by the ALU
	ALU alu = ALU_initialize();			// initialize the ALU		
	if (rf != NULL && sw != NULL && alu != NULL)
		printf("Success\n");	
		
	
	
	//*********************************************************************
	// Load register file with values
	//*********************************************************************
	
	BOOLEAN loop_load = TRUE;
	while (loop_load == TRUE)
	{
		char temp[10];
		uchar reg;
		ushort reg_val;
		
		err = display_registerFile(rf);		// show reg file
		err = display_statusBits(sw);		// show status bits
		
		// prompt for load.
		printf("Load a value into a register? (N to skip) ");
		scanf("%8s", temp);
		temp[1] = '\0';		
		
		if(toupper(temp[0]) != 'N')
		{
			// get register number
			BOOLEAN loop_reg;
			do
			{	
				loop_reg = FALSE;				
				
				printf("\nChoose a register ($R0-$RF) to load:  $R");
				int temp;
				scanf("%8X", &temp);								
				reg = (uchar) temp;
				
				if(reg >= REGISTER_COUNT)
				{
					printf("Invalid register input.  Please try again.\n");
					loop_reg = TRUE;
				}
				
			} while(loop_reg == TRUE);
			
			// get register value
			BOOLEAN loop_val;
			do
			{	
				loop_val = FALSE;
				
				printf("Enter a 16-bit hexadecimal value (FFFF):  ");
				int temp;
				scanf("%8X", &temp);						
				reg_val = (ushort) temp;
				
				if (reg_val > 0xFFFF)	// test to see if input was 16 bit hex
				{	
					printf("Invalid operand input.  Please try again.\n");
					loop_val = TRUE;					
				}
				else
				{
					// load register with value
					printf("Loading $R%X with 0x%X\n", reg, reg_val);
					registerFile_putRegValue(rf, reg, reg_val);
				}
				
			} while (loop_val == TRUE);
			
		}
		else
		{
			loop_load = FALSE;
		}
	}
	
	
	//*********************************************************************
	// Main Test Loop
	//*********************************************************************	
	
	BOOLEAN loop_flag;
	do
	{		
		int test;
		loop_flag = FALSE;
		
		printf("\nSelect test from one of the following operations:\n\n");
		printf("Arithmetic operations:\n");
		printf("     1 - Addition        3 - Multiplication\n");
		printf("     2 - Subtraction     4 - Division\n");		
		printf("                         5 - Negation\n\n");
		printf("Logical operations:\n");
		printf("     6 - AND             9 - NOT\n");
		printf("     7 - OR             10 - Shift Left\n");
		printf("     8 - XOR            11 - Shift Right\n\n");
		printf("Enter operation number (1-11) or 0 to Exit:  ");
		
		int temp;
		scanf("%8d", &temp);
		test = temp;
		
		switch(test)
		{
			case 0:
				printf("\nExiting program.\n");
				break;
			case 1:
				printf("\nAddition operation selected.\n");
				err = ALU_selectOperation(alu, OP_ADD);				
				err = test_doubleOp(alu, rf, sw);
				if(err == 0)
					loop_flag = TRUE;
				break;
			case 2:
				printf("\nSubtraction operation selected.\n");
				err = ALU_selectOperation(alu, OP_SUB);
				err = test_doubleOp(alu, rf, sw);
				if(err == 0)
					loop_flag = TRUE;				
				break;
			case 3:
				printf("\nMultiplication operation selected.\n");
				err = ALU_selectOperation(alu, OP_MUL);
				err = test_doubleOp(alu, rf, sw);				
				if(err == 0)
					loop_flag = TRUE;				
				break;
			case 4:
				printf("\nDivision operation selected.\n");
				err = ALU_selectOperation(alu, OP_DIV);
				err = test_doubleOp(alu, rf, sw);
				if(err == ALU_DIVIDE_BY_ZERO)
				{
					printf("\n ** DIV-BY-ZERO Error ** - operation not carried out.\n");
					loop_flag = TRUE;
				} else if(err == 0)
					loop_flag = TRUE;					
				break;	
			case 5:
				printf("\nNegation operation selected.\n");
				err = ALU_selectOperation(alu, OP_NEG);
				err = test_singleOp(alu, rf, sw);
				if(err == 0)
					loop_flag = TRUE;
				break;
			case 6:
				printf("\nLogical AND operation selected.\n");
				err = ALU_selectOperation(alu, OP_AND);
				err = test_doubleOp(alu, rf, sw);
				if(err == 0)
					loop_flag = TRUE;
				break;
			case 7:
				printf("\nLogical OR operation selected.\n");
				err = ALU_selectOperation(alu, OP_OR);
				err = test_doubleOp(alu, rf, sw);
				if(err == 0)
					loop_flag = TRUE;
				break;
			case 8:
				printf("\nLogical XOR operation selected.\n");
				err = ALU_selectOperation(alu, OP_XOR);
				err = test_doubleOp(alu, rf, sw);
				if(err == 0)
					loop_flag = TRUE;
				break;
			case 9:
				printf("\nLogical NOT operation selected.\n");
				err = ALU_selectOperation(alu, OP_NOT);
				err = test_singleOp(alu, rf, sw);
				if(err == 0)
					loop_flag = TRUE;
				break;
			case 10:
				printf("\nBitwise Shift Left operation selected.\n");
				err = ALU_selectOperation(alu, OP_SHL);
				err = test_doubleOp(alu, rf, sw);
				if(err == 0)
					loop_flag = TRUE;
				break;
			case 11:
				printf("\nBitwise Shift Right operation selected.\n");
				err = ALU_selectOperation(alu, OP_SHR);
				err = test_doubleOp(alu, rf, sw);
				if(err == 0)
					loop_flag = TRUE;
				break;					
			default:
				printf("\nInvalid input. Please try again.\n");
				break;
		}
		
		err = display_registerFile(rf);		// show reg file
		err = display_statusBits(sw);		// show status bits
				
	} while (loop_flag == TRUE);
	

	return err;
}
